﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.Direct3D9;
using SlimDX;
using System.Runtime.InteropServices;

namespace SlimWater
{
    struct SkyboxVertex
    {
        public Vector3 Position;
        public Vector2 TexCoord0;

        public SkyboxVertex(Vector3 Position, Vector2 TexCoord0)
        {
            this.Position = Position;
            this.TexCoord0 = TexCoord0;
        }
    }

    class Skybox
    {
        Device device;

        VertexBuffer vb;
        IndexBuffer ib;

        Effect effect;
        Texture texture;

        Matrix world;
        Matrix scale;

        VertexDeclaration vertexDecl;

        public Skybox(Device device, float size)
        {
            this.device = device;

            scale = Matrix.Scaling(size, size, size);

            vb = new VertexBuffer(device, 24 * Marshal.SizeOf(typeof(SkyboxVertex)),
                Usage.WriteOnly, VertexFormat.None, Pool.Managed);
            vb.DebugName = "Skybox VertexBuffer";

            float offset = 1f / 6f;
            var stream = vb.Lock(0, 0, LockFlags.None);
            stream.WriteRange(new[] {
	            // front
                new SkyboxVertex(new Vector3(-1.0f, -1.0f, -1.0f), new Vector2(0 * offset, 1)),
                new SkyboxVertex(new Vector3(-1.0f, 1.0f, -1.0f), new Vector2(0 * offset, 0)),
                new SkyboxVertex(new Vector3(1.0f, -1.0f, -1.0f), new Vector2(1 * offset, 1)),
                new SkyboxVertex(new Vector3(1.0f, 1.0f, -1.0f), new Vector2(1 * offset, 0)),
                // right
                new SkyboxVertex(new Vector3(1.0f, -1.0f, -1.0f), new Vector2(1 * offset, 1)),
                new SkyboxVertex(new Vector3(1.0f, 1.0f, -1.0f), new Vector2(1 * offset, 0)),
                new SkyboxVertex(new Vector3(1.0f, -1.0f, 1.0f), new Vector2(2 * offset, 1)),
                new SkyboxVertex(new Vector3(1.0f, 1.0f, 1.0f), new Vector2(2 * offset, 0)),
                // back
                new SkyboxVertex(new Vector3(1.0f, -1.0f, 1.0f), new Vector2(2 * offset, 1)),
                new SkyboxVertex(new Vector3(1.0f, 1.0f, 1.0f), new Vector2(2 * offset, 0)),
                new SkyboxVertex(new Vector3(-1.0f, -1.0f, 1.0f), new Vector2(3 * offset, 1)),
                new SkyboxVertex(new Vector3(-1.0f, 1.0f, 1.0f), new Vector2(3 * offset, 0)),
                // left
                new SkyboxVertex(new Vector3(-1.0f, -1.0f, 1.0f), new Vector2(3 * offset, 1)),
                new SkyboxVertex(new Vector3(-1.0f, 1.0f, 1.0f), new Vector2(3 * offset, 0)),
                new SkyboxVertex(new Vector3(-1.0f, -1.0f, -1.0f), new Vector2(4 * offset, 1)),
                new SkyboxVertex(new Vector3(-1.0f, 1.0f, -1.0f), new Vector2(4 * offset, 0)),
                // top
                new SkyboxVertex(new Vector3(-1.0f, 1.0f, -1.0f), new Vector2(4 * offset, 1)),
                new SkyboxVertex(new Vector3(-1.0f, 1.0f, 1.0f), new Vector2(4 * offset, 0)),
                new SkyboxVertex(new Vector3(1.0f, 1.0f, -1.0f), new Vector2(5 * offset, 1)),
                new SkyboxVertex(new Vector3(1.0f, 1.0f, 1.0f), new Vector2(5 * offset, 0)),
                // bottom
                new SkyboxVertex(new Vector3(-1.0f, -1.0f, 1.0f), new Vector2(5 * offset, 1)),
                new SkyboxVertex(new Vector3(-1.0f, -1.0f, -1.0f), new Vector2(5 * offset, 0)),
                new SkyboxVertex(new Vector3(1.0f, -1.0f, 1.0f), new Vector2(1, 1)),
                new SkyboxVertex(new Vector3(1.0f, -1.0f, -1.0f), new Vector2(1, 0)),
			});
            vb.Unlock();

            ib = new IndexBuffer(device, 6 * 6 * 4, Usage.None, Pool.Default, false);
            ib.DebugName = "Skybox IndexBuffer";
            stream = ib.Lock(0, 0, LockFlags.None);
            stream.WriteRange(new[] {
                0, 1, 2, 1, 3, 2, // front
                4, 5, 6, 5, 7, 6, // right
                8, 9, 10, 9, 11, 10, // back
                12, 13, 14, 13, 15, 14, // left
                16, 17, 18, 17, 19, 18, // top
                20, 21, 22, 21, 23, 22 // bottom
            });
            ib.Unlock();

            vertexDecl = new VertexDeclaration(device, new[] {
				new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), 
                new VertexElement(0, 12, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0), 
				VertexElement.VertexDeclarationEnd
        	});

            effect = Effect.FromFile(device, "../../data/skybox.fx", ShaderFlags.None);

            texture = Texture.FromFile(device, "../../data/skybox.png");
            texture.DebugName = "Skybox Texture";
        }

        public void Render()
        {
            Matrix wvp = world * device.GetTransform(TransformState.View) * device.GetTransform(TransformState.Projection);

            device.SetStreamSource(0, vb, 0, Marshal.SizeOf(typeof(SkyboxVertex)));
            device.Indices = ib;
            device.VertexDeclaration = vertexDecl;

            effect.Technique = "SkyboxTechnique";
            effect.SetValue("xWorldViewProj", wvp);
            effect.SetTexture("xSkyTexture", texture);

            effect.Begin(0);
            effect.BeginPass(0);
            {
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 24, 0, 12);
            }
            effect.EndPass();
            effect.End();
        }

        public void Update(Vector3 v) { world = scale * Matrix.Translation(v); }
    }
}
